////////////////////////////////////////////////////////////////////////////////
//
//	Original file Name: SpriteManager.java
//	Originating Author: JWhitefield
//	
//	Description: Manages the sprite sheets and singular sprite's within that 
//
////////////////////////////////////////////////////////////////////////////////

package com.jjw.game.graphics;

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.HashMap;

import javax.imageio.ImageIO;

/**
 * Manages the sprite sheets and singular sprite's within that 
 * 
 * @author JWhitefield
 *
 */
public class SpriteManager {

	//--------------------------------------------------------------------------
	//
	//Properties
	//
	//--------------------------------------------------------------------------
		
	//----------------------------------
	// Public
	//----------------------------------
		
	/**
	 * Static reference to the sprite asset directory 
	 */
	static final String ASSET_SPRITE_DIRECTORY = "assets/images/sprites";
	
	/**
	 * Static reference to the character sprite asset directory  
	 */
	static final String ASSET_SPRITE_CHARACTERS_DIRECTORY = "characters";

	/**
	 * Static reference to the tile sprite asset directory  
	 */
	static final String ASSET_SPRITE_TILES_DIRECTORY = "tiles";
	
	//----------------------------------
	//  Getters / Setters
	//----------------------------------
	
	//----------------------------------
	// Private
	//----------------------------------
	
	/**
	 * Singleton object for the sound manager 
	 */
	private static SpriteManager instance;	
		
	/**
	 * List of the tiles sprite's available, grouped by the file name they where loaded with  
	 */
	private HashMap<String, SpriteSheet> tileSprites;
	
	/**
	 * List of the character sprite's available grouped by the file name they where loaded with 
	 */
	private HashMap<String, SpriteSheet> characterSprites;
	
	//--------------------------------------------------------------------------
	//
	//  Methods
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	// Public
	//----------------------------------
	
	/**
	 * Hide constructor so that classes outside of this one cannot create an instance of it 
	 */
	SpriteManager() {
		
		//Start loading the sprite's for the sprite manager
		this.loadSprites();
	}
	
	/**
	 * Get and instance of the sprite manager 
	 * 
	 * @return	return that instance 
	 */
	public static SpriteManager getInstance() {
		
		//If we don't have an instance of it 
		if( instance == null ) {
			instance = new SpriteManager();
		}
		
		//Return the instance of the sound manager 
		return instance;
	}
	
	/**
	 * Gets a tile sprite based on the sheet reference and coordinates 
	 * 
	 * @param sheetReference 	the sprite sheet reference we are looking for the asset in
	 * @param row 				the row of the sprite sheet the sprite is on 
	 * @param column 			the column of the sprite sheet the sprite is on
	 *  
	 * @return					the found sprite if it couldn't be found null will be returned
	 */
	public Sprite getTileSprite( String sheetReference, int row, int column ) {
		
		//Call the function to find the title sprite in the list 
		return this.findSpriteInSpriteSheetList( this.tileSprites, sheetReference, row, column);
	}
	
	/**
	 * Gets a character sprite based on the sheet reference and coordinates 
	 * 
	 * @param sheetReference 	the sprite sheet reference we are looking for the asset in
	 * @param row 				the row of the sprite sheet the sprite is on 
	 * @param column 			the column of the sprite sheet the sprite is on
	 *  
	 * @return					the found sprite if it couldn't be found null will be returned
	 */
	public Sprite getCharacterSprite( String sheetReference, int row, int column ) {
		
		//Call the function to find the character sprite in the list 
		return this.findSpriteInSpriteSheetList( this.characterSprites, sheetReference, row, column);
	}
	
	//----------------------------------
	// Private
	//----------------------------------
	
	/**
	 * Load all the sprite's into the application 
	 */
	private void loadSprites() {
		
		//First load the tile sprite's 
		this.loadTileSprites();
		
		//Now load character sprite's
		this.loadCharacterSprties();
		
		//Log that the sprite's have loaded 
		System.out.println("Sprites Loaded");
	}
	
	/**
	 * Loads the tile sprite's into the application 
	 */
	private void loadTileSprites() {
		
		//Create a new tile sprite's list
		if( this.tileSprites == null )
			this.tileSprites = new HashMap<String, SpriteSheet>();
			
		//Make sure the list is empty
		this.tileSprites.clear();
		
		//Call the load sprites from sheet to load all the tile sprite's
		this.loadSpriteFromSheets( this.tileSprites, ASSET_SPRITE_TILES_DIRECTORY, 32, 32);
	}
	
	/**
	 * Loads the character sprite's into the application 
	 */
	private void loadCharacterSprties() {
		
		//Create a new character sprite's list
		if( this.characterSprites == null )
			this.characterSprites = new HashMap<String, SpriteSheet>();
			
		//Make sure the list is empty
		this.characterSprites.clear();
		
		//Call the load sprite's from sheet to load all the character sprite's
		this.loadSpriteFromSheets( this.characterSprites, ASSET_SPRITE_CHARACTERS_DIRECTORY, 32, 48);
	}
	
	/**
	 * Loads the sprite's into the lists passed in 
	 * 
	 * @param sprites		the list the sprite's are to be loaded into
	 * @param directory		the directory where the sprite sheets can be found
	 * @param spriteWidth	the width of the sprite's in these file
	 * @param spriteHeight	the height of the sprite's in these file 
	 */
	private void loadSpriteFromSheets( HashMap<String, SpriteSheet> sprites, String directory, int spriteWidth, int spriteHeight ) {
		
		//Get a list of the spritesheet's in the tile asset folder 
		File spriteFolder = new File(ASSET_SPRITE_DIRECTORY + File.separator + directory );
		
		//If the folder dosn't exist then stop
		if( !spriteFolder.exists() )
			return;
		
		//Loop through the list adding the sprite's to the list
		for( File spriteFile : spriteFolder.listFiles() ) {
			
			//Check we can work with this file i.e. is it a file and can we read it 
			if( spriteFile.isFile() && spriteFile.canRead()){
				
				//Check its a PNG as we only want PNG's
				if( spriteFile.getName().toLowerCase().contains(".png") ){
					
					//Get the sprite sheet as an image 
					BufferedImage spriteSheetImage = this.readFileAsBufferedImage( spriteFile );
					
					//If we got our sprite sheet then add it to the list
					if( spriteSheetImage != null ) {
						
						//Create the sprite sheet
						SpriteSheet spriteSheet = new SpriteSheet(spriteSheetImage, spriteWidth, spriteHeight);
						
						//Get a sprite sheet reference 
						String spriteSheetReference = this.createSpriteSheetReference( spriteFile.getName() );
						
						//Record the sprite's in a list 
						sprites.put( spriteSheetReference, spriteSheet);
					}
				}	
			}
		}		
	}
	
	/**
	 * Returns the image extracted for an image file
	 * 
	 * @param imageFile		the image file we wish to get the image for
	 * 
	 * @return				the image extracted from the file  
	 */
	private BufferedImage readFileAsBufferedImage( File imageFile ) {
		
		//The image we will return 
		BufferedImage returnedImage = null;
		
		//Enclose within a try if loading fails 
		try{	
			//Reads the image file and sets it as a buffered image
			returnedImage = ImageIO.read( imageFile );
			
		}catch (Exception e) {
			
			//Print the stack trace 
			e.printStackTrace();
		}
		
		//Return the image 
		return returnedImage;
	}
	
	/**
	 * Creates a clean sprite sheet reference which is uniform  
	 * 
	 * @param	spriteSheetFileName		the name of the sprite sheets file name 
	 * @return							a cleaned up version of the passed in string so that we can compare like strings 
	 */
	private String createSpriteSheetReference( String spriteSheetFileName ) {
		
		//Turns the reference to lower case, removes file extensions and trims the string
		return spriteSheetFileName.toLowerCase().replace(".png", "").trim();
	}
	
	/**
	 * Finds the sprite form the passed in sprite sheet list 
	 * 
	 * @param sprites			the list of sprite's being searched
	 * @param sheetReference	the sheet reference we are looking for 
	 * @param row				the row the sprite we want is in 
	 * @param column			the column the sheet we want is in 
	 * 	
	 * @return					the found sprite or null if it couldn't be found 
	 */
	private Sprite findSpriteInSpriteSheetList( HashMap<String, SpriteSheet> sprites, String sheetReference, int row, int column ) {
		
		//The sprite we have found 
		Sprite foundSprite = null;
		
		//If the list is null return null
		if( sprites == null )
			return null;
		
		//Use the same function used to create the reference to format the passed in string
		sheetReference = this.createSpriteSheetReference( sheetReference );
		
		//Get the sprite's for this sheet
		SpriteSheet spriteSheet = sprites.get( sheetReference );
		
		//If the spites from the sheet is null then return null
		if( spriteSheet == null )
			return null;
		
		//Get the sprite from the sprite sheet 
		foundSprite = spriteSheet.getImageAt(row, column);
		
		//Return the found sprite
		return foundSprite;
	}
	
	//----------------------------------
	// Overridden / Implemented
	//----------------------------------
	
	
}
